/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */

package br.com.murilojava.compilador.scanner;

import java.io.BufferedReader;
import java.util.ArrayList;
import java.util.List;

import br.com.murilojava.compilador.commom.TabelaIdentificadores;
import br.com.murilojava.compilador.model.TipoToken;
import br.com.murilojava.compilador.model.Token;

/**
 * 
 * @author Murilo
 */
public class Scanner {

	private static final boolean DEBUG = true;

	private Token atual;

	private Token proximo;

	private List<Token> buffer = new ArrayList<Token>();

	private BufferedReader input;

	private int lineCount = 0;

	private boolean estaEmComentarioBloco = false;

	public boolean analisar() throws Exception {

		// Limpa as variaveis relacionadas com o processo.
		buffer.clear();
		estaEmComentarioBloco = false;

		if (input == null)
			return false;

		String line = null;
		lineCount = 1;
		while ((line = input.readLine()) != null) {
			lineCount++;
			StringBuilder builder = new StringBuilder(line);

			while (builder.length() > 0) {
				char ch = builder.charAt(0);

				debug("CH LIDO scannner.analisar() '" + ch + "'");

				if (estaEmComentarioBloco) {
					debug("ESTA_EM_COMENTARIO_BLOCO = TRUE comentario bloco estado.");
					comentarioBlocoEstado(builder);
					continue;
				}

				if (Character.isLetter(ch)) {
					debug("identificador estado.");
					Token token = identificadorEstado(builder);
					if (token != null
							&& TabelaIdentificadores.ehPalavraReservada(token
									.getLexema())) {
						token.setTipo(TipoToken.RESERVADO);
					}
					adicionarToken(token);
					continue;
				}

				if (Character.isDigit(ch)) {
					debug("número estado.");
					adicionarToken(numeroEstado(builder));
					continue;
				}

				if (TabelaIdentificadores.ehBarra(ch)) {
					debug("descisao barra estado.");
					Token token = descisaoBarraEstado(builder);
					// Se o token for comentario vem nulo logo é desconsiderado
					if (token != null)
						adicionarToken(token);
					continue;
				}

				if (TabelaIdentificadores.ehOMenos(ch)) {
					debug("decisao menos estado.");
					adicionarToken(decisaoMenosEstado(builder));
					continue;
				}

				if (TabelaIdentificadores.ehOperador(ch)) {
					debug("operador estado.");
					adicionarToken(operadorEstado(builder));
					continue;
				}

				if (TabelaIdentificadores.ehDelimitador(ch)) {
					debug("delimitador estado.");
					adicionarToken(delimitadorEstado(builder));
					continue;
				}

				if (TabelaIdentificadores.ehDelimitadorString(ch)) {
					debug("string estado.");
					adicionarToken(stringEstado(builder));
					continue;
				}
				if (TabelaIdentificadores.ehDelimitadorChar(ch)) {
					debug("char estado.");
					adicionarToken(charEstado(builder));
					continue;
				}

				if (TabelaIdentificadores.ehCaracterTrash(ch)) {
					debug("trash estado.");
					builder.deleteCharAt(0);
					continue;
				}

				// Não identificou o caracater
				debug("caracter invalido estado.");

				Token tok = new Token();
				tok.setErroMsg("Caracter não reconhecido");
				tok.setLexema("" + ch);
				tok.setErro(true);
				tok.setTipo(TipoToken.IDENTIFICADOR);
				adicionarToken(tok);
				builder.deleteCharAt(0);
			}
		}

		return true;
	}

	private Token charEstado(StringBuilder builder) {
		String erro = "";
		String valor = "";

		builder.deleteCharAt(0);

		char ch;
		while (builder.length() > 0
				&& !TabelaIdentificadores.ehDelimitadorChar(ch = builder
						.charAt(0))) {
			valor += ch;

			if (!TabelaIdentificadores.ehCharacterStringValido(ch)) {
				erro += "Caracter inválido '" + ch + "'.";
			}
			builder.deleteCharAt(0);
		}

		if (builder.length() > 0) {
			builder.deleteCharAt(0);

			if (valor.length() > 1) {
				erro += "Char com formato inválido.";
			}
		} else {
			erro += "Fim do char não encontrado.";
		}

		Token token = new Token();
		token.setLexema(valor);
		token.setTipo(TipoToken.CHAR);
		if (!erro.isEmpty()) {
			token.setErro(true);
			token.setErroMsg(erro);
		}
		return token;
	}

	private Token stringEstado(StringBuilder builder) {

		String erro = "";
		String valor = "";

		builder.deleteCharAt(0);

		char ch;
		while (builder.length() > 0
				&& !TabelaIdentificadores.ehDelimitadorString(ch = builder
						.charAt(0))) {
			valor += ch;

			if (!TabelaIdentificadores.ehCharacterStringValido(ch)) {
				erro += "Caracter invalido '" + ch + "'.";
			}
			builder.deleteCharAt(0);
		}

		if (builder.length() > 0) {
			builder.deleteCharAt(0);
		} else {
			erro += "Fim da String não encontrado.";
		}

		Token token = new Token();
		token.setLexema(valor);
		token.setTipo(TipoToken.STRING);
		if (!erro.isEmpty()) {
			token.setErro(true);
			token.setErroMsg(erro);
		}
		return token;
	}

	private Token decisaoMenosEstado(StringBuilder builder) {
		if (builder.length() > 1) {
			char segundoChar = builder.charAt(1);

			if (Character.isDigit(segundoChar))
				return numeroEstado(builder);
		}

		return operadorEstado(builder);
	}

	private Token descisaoBarraEstado(StringBuilder builder) {

		if (builder.length() > 1) {
			char segundoChar = builder.charAt(1);

			if (TabelaIdentificadores.ehBarra(segundoChar)) {
				return comentarioLinhaEstado(builder);
			}

			if (TabelaIdentificadores.ehAsterisco(segundoChar)) {
				return comentarioBlocoEstado(builder);
			}
		}

		return operadorEstado(builder);
	}

	private Token comentarioBlocoEstado(StringBuilder builder) {

		if (builder.length() > 1
				&& TabelaIdentificadores.ehBarra(builder.charAt(0))
				&& TabelaIdentificadores.ehAsterisco(builder.charAt(1))) {

			// Deleta o primeiro e o segundo
			builder.deleteCharAt(0);
			builder.deleteCharAt(0);

			estaEmComentarioBloco = true;
		}

		while (builder.length() > 0) {
			char ch = builder.charAt(0);
			builder.deleteCharAt(0);

			if (TabelaIdentificadores.ehAsterisco(ch)) {
				if (builder.length() > 0
						&& TabelaIdentificadores.ehBarra(builder.charAt(0))) {
					builder.deleteCharAt(0);
					estaEmComentarioBloco = false;
					return null;
				}
			}
		}

		return null;
	}

	private Token comentarioLinhaEstado(StringBuilder builder) {
		while (builder.length() > 0)
			builder.deleteCharAt(0);

		return null;
	}

	private Token operadorEstado(StringBuilder builder) {
		if (builder.length() > 1) {
			char segundoChar = builder.charAt(1);
			if (TabelaIdentificadores.ehOperador(segundoChar)) {
				String opComposto = builder.charAt(0) + "" + builder.charAt(1);

				if (TabelaIdentificadores.ehOperadoComposto(opComposto)) {
					// É necessário deletar Duas vezes porque é o primeiro e o
					// segundo char;
					builder.deleteCharAt(0);
					builder.deleteCharAt(0);

					Token token = new Token();
					token.setLexema(opComposto);
					token.setTipo(TipoToken.OPERADOR);

					return token;
				}
			}
		}

		String erroMsg = "";
		boolean erro = false;
		if (!TabelaIdentificadores.ehOperadorSimples(builder.charAt(0))) {
			erro = true;
			erroMsg = "Operador mal formado.";
		}

		Token token = new Token();
		token.setLexema(builder.charAt(0) + "");
		token.setTipo(TipoToken.OPERADOR);
		token.setErro(erro);
		token.setErroMsg(erroMsg);
		builder.deleteCharAt(0);

		return token;

	}

	private Token delimitadorEstado(StringBuilder builder) {
		char ch = builder.charAt(0);
		builder.deleteCharAt(0);

		Token token = new Token();
		token.setTipo(TipoToken.DELIMITADOR);
		token.setLexema(String.valueOf(ch));

		return token;
	}

	private void debug(String string) {
		if (DEBUG)
			System.out.println(string);
	}

	private Token numeroEstado(StringBuilder builder) {

		String inteiro = "";
		String decimal = "";

		boolean erro = false;

		char ch;

		if (builder.length() > 0
				&& TabelaIdentificadores.ehOMenos(builder.charAt(0))) {

			inteiro += "-";
			builder.deleteCharAt(0);
		}

		while (builder.length() > 0
				&& TabelaIdentificadores.ehNumero(ch = builder.charAt(0))) {
			inteiro += ch;
			builder.deleteCharAt(0);
		}

		boolean deveTerDecimal = false;
		if (builder.length() > 0 && builder.charAt(0) == '.') {
			deveTerDecimal = true;
			builder.deleteCharAt(0);
		}

		if (deveTerDecimal) {
			while (builder.length() > 0
					&& TabelaIdentificadores.ehNumero(ch = builder.charAt(0))) {
				decimal += ch;
				builder.deleteCharAt(0);
			}
		}

		if (deveTerDecimal && decimal.isEmpty()) {
			erro = true;
		}

		while (builder.length() > 0
				&& !TabelaIdentificadores.ehMarcador(ch = builder.charAt(0))) {

			builder.deleteCharAt(0);

			erro = true;
			if (deveTerDecimal)
				decimal += ch;
			else
				inteiro += ch;
		}

		if (deveTerDecimal) {
			inteiro += "." + decimal;
		}

		Token token = new Token();
		token.setLexema(inteiro);
		token.setTipo(TipoToken.NUMERO);
		token.setErro(erro);

		return token;
	}

	public Token getToken() {

		return atual;
	}

	public Token identificadorEstado(StringBuilder builder) {

		String nomeIdentificador = "";
		boolean erro = false;

		char ch;
		while (builder.length() > 0
				&& !TabelaIdentificadores.ehMarcador(ch = builder.charAt(0))) {
			nomeIdentificador += ch;
			builder.deleteCharAt(0);

			if (!TabelaIdentificadores.ehCaracterValidoIdentificador(ch))
				erro = true;
		}

		Token token = new Token();
		token.setLexema(nomeIdentificador);
		token.setTipo(TipoToken.IDENTIFICADOR);
		token.setErro(erro);

		return token;
	}

	private void adicionarToken(Token token) {
		if (token != null) {
			token.setLinha(lineCount);
			buffer.add(token);
		}
	}

	// public static void main(String[] args) throws Exception {
	//
	// Scanner sca = new Scanner();
	// sca.setInput(new BufferedReader(new StringReader(
	// "muri~~lo bdds8arbosa d1a2 s34i56l67va 1 2.0 3a 4.5")));
	// sca.analisar();
	//
	// System.out.println(sca.buffer);
	// }

	public void setInput(BufferedReader input) {
		this.input = input;
	}

	public List<Token> getBuffer() {
		return buffer;
	}

}
